package com.furtherstar.auth.service.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import com.furtherstar.auth.controller.dto.AppRoleGroup;
import com.furtherstar.auth.controller.dto.RoleQuery;
import com.furtherstar.auth.controller.dto.RoleResourceSetting;
import com.furtherstar.auth.dao.enums.Disabled;
import com.furtherstar.auth.dao.mapper.AppRoleMapper;
import com.furtherstar.auth.dao.mapper.AppRoleResourceMapper;
import com.furtherstar.auth.dao.mapper.UserRoleMapper;
import com.furtherstar.auth.dao.model.App;
import com.furtherstar.auth.dao.model.AppRole;
import com.furtherstar.auth.dao.model.AppRoleExample;
import com.furtherstar.auth.dao.model.AppRoleExample.Criteria;
import com.furtherstar.auth.dao.model.AppRoleResource;
import com.furtherstar.auth.dao.model.AppRoleResourceExample;
import com.furtherstar.auth.dao.model.UserRoleExample;
import com.furtherstar.auth.service.AppRoleService;
import com.furtherstar.auth.service.AppService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageSerializable;
import com.yummy.convention.exception.AssertUtil;
import com.yummy.convention.lang.IDGenerator;
import com.yummy.security.User;
import com.yummy.web.context.RequestContext;

@Service
public class AppRoleServiceImpl implements AppRoleService{
	
	@Autowired
	private AppRoleMapper appRoleMapper;
	@Autowired
	private AppRoleResourceMapper appRoleResourceMapper;
	@Autowired
	private UserRoleMapper userRoleMapper;
	@Autowired
	private AppService appService;

	@Override
	public Long add(AppRole role) {
		AssertUtil.isTrue(!exist(role),"已存在");
		createMark(role);
		appRoleMapper.insert(role);
		return role.getId();
	}

	private void createMark(AppRole role) {
		role.setId(IDGenerator.getId());
		role.setDisabled(Disabled.normal.ordinal());
		User user = RequestContext.getCurrentUser();
		role.setLmNickname(user.getNickname());
		role.setLmTime(new Date());
		role.setLmUserid((Long) user.getUserid());
	}

	private boolean exist(AppRole role) {
		AppRoleExample example = new AppRoleExample();
		Criteria criteria = example.createCriteria().andAppnameEqualTo(role.getAppname())
			.andNameEqualTo(role.getName());
		if(role.getId() != null) {
			criteria.andIdNotEqualTo(role.getId());
		}
		return false;
	}

	@Override
	public Long update(AppRole role) {
		AssertUtil.isTrue(!exist(role),"已存在");
		optMark(role);
		int row = appRoleMapper.updateByPrimaryKeySelective(role);
		AssertUtil.isTrue(row == 1,"找不到记录");
		return role.getId();
	}

	private void optMark(AppRole role) {
		User user = RequestContext.getCurrentUser();
		role.setLmNickname(user.getNickname());
		role.setLmTime(new Date());
		role.setLmUserid((Long) user.getUserid());
	}

	@Override
	public Long enable(Long roleId) {
		AppRole role = new AppRole();
		role.setId(roleId);
		role.setDisabled(Disabled.normal.ordinal());
		optMark(role);
		int row = appRoleMapper.updateByPrimaryKeySelective(role);
		AssertUtil.isTrue(row == 1,"找不到记录");
		return roleId;
	}

	@Override
	public Long disable(Long roleId) {
		AppRole role = new AppRole();
		role.setId(roleId);
		role.setDisabled(Disabled.disabled.ordinal());
		optMark(role);
		int row = appRoleMapper.updateByPrimaryKeySelective(role);
		AssertUtil.isTrue(row == 1,"找不到记录");
		return roleId;
	}

	@Override
	@Transactional
	public Boolean delete(Long roleId) {
		int row = appRoleMapper.deleteByPrimaryKey(roleId);
		AssertUtil.isTrue(row == 1,"找不到记录");
		deleteRelated(roleId);
		return true;
	}

	private void deleteRelated(Long roleId) {
		deleteAppResourcesByRole(roleId);
		deleteUserRolesByRole(roleId);
	}

	private void deleteUserRolesByRole(Long roleId) {
		UserRoleExample example = new UserRoleExample();
		example.createCriteria().andRoleIdEqualTo(roleId);
		userRoleMapper.deleteByExample(example);
	}

	private void deleteAppResourcesByRole(Long roleId) {
		AppRoleResourceExample example = new AppRoleResourceExample();
		example.createCriteria().andRoleIdEqualTo(roleId);
		appRoleResourceMapper.deleteByExample(example);
	}

	@Override
	public PageSerializable<AppRole> queryPage(RoleQuery roleQuery) {
		PageHelper.startPage(roleQuery.getPageNum(), roleQuery.getPageSize());
		List<AppRole> list = queryList(roleQuery);
		return new PageSerializable<>(list);
	}

	private List<AppRole> queryList(RoleQuery roleQuery) {
		AppRoleExample example = new AppRoleExample();
		Criteria criteria = example.createCriteria().andAppnameEqualTo(roleQuery.getAppname());
		if(StringUtils.isNotBlank(roleQuery.getName())) {
			criteria.andNameLike("%"+roleQuery.getName()+"%");
		}
		return appRoleMapper.selectByExample(example);
	}

	@Override
	public PageSerializable<AppRole> queryAvaliablePage(RoleQuery roleQuery) {
		PageHelper.startPage(roleQuery.getPageNum(), roleQuery.getPageSize());
		List<AppRole> list = queryAvaliableList(roleQuery);
		return new PageSerializable<>(list);
	}

	private List<AppRole> queryAvaliableList(RoleQuery roleQuery) {
		AppRoleExample example = new AppRoleExample();
		Criteria criteria = example.createCriteria()
				.andAppnameEqualTo(roleQuery.getAppname())
				.andDisabledEqualTo(Disabled.normal.ordinal());
		if(StringUtils.isNotBlank(roleQuery.getName())) {
			criteria.andNameLike("%"+roleQuery.getName()+"%");
		}
		return appRoleMapper.selectByExample(example);
	}

	@Override
	@Transactional
	public Boolean setResources(RoleResourceSetting roleResourceSetting) {
		Long roleId = roleResourceSetting.getRoleId();
		AppRole role = appRoleMapper.selectByPrimaryKey(roleId);
		AssertUtil.notNull(role);
		List<AppRoleResource> roleResources = getRoleResources(role,roleResourceSetting.getResourceIds());
		deleteOldRoleResources(roleId);
		if(!CollectionUtils.isEmpty(roleResources)) {
			appRoleResourceMapper.insertBatch(roleResources);
		}
		return true;
	}

	private void deleteOldRoleResources(Long roleId) {
		deleteAppResourcesByRole(roleId);
	}

	private List<AppRoleResource> getRoleResources(AppRole role, List<Long> resourceIds) {
		return resourceIds.stream().map(r -> {
			AppRoleResource arr = new AppRoleResource();
			arr.setId(IDGenerator.getId());
			arr.setAppname(role.getAppname());
			arr.setRoleId(role.getId());
			arr.setResourceId(r);
			createMark(arr);
			return arr;
		}).toList();
	}

	private void createMark(AppRoleResource arr) {
		arr.setId(IDGenerator.getId());
		User user = RequestContext.getCurrentUser();
		arr.setLmNickname(user.getNickname());
		arr.setLmTime(new Date());
		arr.setLmUserid((Long) user.getUserid());
	}

	@Override
	public List<Long> getResources(Long roleId) {
		List<AppRoleResource> roleResources = getRoleResourcesByRole(roleId);
		if(CollectionUtils.isEmpty(roleResources)) {
			return Collections.emptyList();
		}
		return roleResources.stream().map(rr -> rr.getResourceId()).toList();
	}

	private List<AppRoleResource> getRoleResourcesByRole(Long roleId) {
		AppRoleResourceExample example = new AppRoleResourceExample();
		example.createCriteria().andRoleIdEqualTo(roleId);
		return appRoleResourceMapper.selectByExample(example);
	}

	@Override
	public List<AppRoleGroup> getGroupAppRoles() {
		List<AppRole> list = appRoleMapper.selectByExample(null);
		if(CollectionUtils.isEmpty(list)) {
			return Collections.emptyList();
		}
		List<String> appnames = list.stream().map(ar -> ar.getAppname()).toList();
		Map<String,App> appMap = appService.getAppMap(appnames);
		Map<String,List<AppRole>> groupRolesMap = list.stream().collect(Collectors.groupingBy(ar -> ar.getAppname()));
		List<AppRoleGroup> groupList = new ArrayList<>();
		for(String appname : groupRolesMap.keySet()) {
			AppRoleGroup group = new AppRoleGroup();
			group.setAppname(appname);
			group.setRoles(groupRolesMap.get(appname));
			group.setAppTitle(appMap.containsKey(appname) ? appMap.get(appname).getAppTitle() : null);
			groupList.add(group);
		}
		return groupList;
	}

}
